﻿using System;
using System.Collections.Generic;
using System.Text;
using DataAccessTecnoPez;
using System.Data.OleDb;
using System.Data;

namespace BussinessLogicTecnoPez.Produccion.Compras.MatrizHomogeneizacion
{
    public class MatrizHomogeneizacion
    {
        #region "Atributos"
        private List<FactorAnalizado> objFactores;
        private List<OfertaAnalizada> objOfertasAnalizadas;
        private List<OfertaDescartada> objOfertasDescartadas;
        private List<FactorAnalizado> objMaximosMinimos;

        private bool booUsarCantidad;
        private decimal decCantidad;
        #endregion

        #region "Propiedades"
        public DataView OfertasDescartadas
        {
            get
            {
                DataTable objDataTable = new DataTable();

                objDataTable.Columns.Add("Oferta");
                objDataTable.Columns.Add("Proveedor");
                objDataTable.Columns.Add("FechaCarga");
                objDataTable.Columns.Add("Vencimiento");
                objDataTable.Columns.Add("Factor");
                objDataTable.Columns.Add("Tipo");
                objDataTable.Columns.Add("Valor");
                objDataTable.Columns.Add("Tolerancia");

                DataRow objRow = null;

                foreach (OfertaDescartada objOferta in objOfertasDescartadas)
                {
                    objRow = objDataTable.NewRow();

                    objRow["Oferta"] = objOferta.IdOferta;
                    objRow["Proveedor"] = objOferta.Proveedor.ToString();
                    objRow["FechaCarga"] = objOferta.FechaCarga.ToShortDateString();
                    objRow["Vencimiento"] = objOferta.FechaVencimientoString;
                    objRow["Factor"] = objOferta.FactorAnalizado.Descripcion;
                    objRow["Tipo"] = objOferta.FactorAnalizado.Tipo;
                    objRow["Valor"] = objOferta.FactorAnalizado.Valor;
                    objRow["Tolerancia"] = objOferta.Tolerancia;

                    objDataTable.Rows.Add(objRow);
                }
                return objDataTable.DefaultView;
            }
        }

        public DataView OfertasAprobadas
        {
            get
            {
                objOfertasAnalizadas.Sort();
                DataTable objDataTable = new DataTable();

                objDataTable.Columns.Add("Oferta");
                objDataTable.Columns.Add("Proveedor");
                objDataTable.Columns.Add("FechaCarga");
                objDataTable.Columns.Add("Vencimiento");

                foreach (FactorAnalizado objFactor in objFactores)
                {
                    //no se incluye la cantidad
                    if (objFactor.IdFactor != -2)
                        objDataTable.Columns.Add(objFactor.Descripcion);
                }

                objDataTable.Columns.Add("Total");

                DataRow objRow = null;

                foreach (OfertaAnalizada objOferta in objOfertasAnalizadas)
                {
                    objRow = objDataTable.NewRow();
                    objRow["Oferta"] = objOferta.IdOferta;
                    objRow["Proveedor"] = objOferta.Proveedor.ToString();
                    objRow["FechaCarga"] = objOferta.FechaCarga.ToShortDateString();
                    objRow["Vencimiento"] = objOferta.FechaVencimientoString;

                    foreach (FactorAnalizado objFactor in objOferta.Factores)
                    {
                        //no se incluye la cantidad
                        if (objFactor.IdFactor != -2)
                            objRow[objFactor.Descripcion] = objFactor.Puntaje;
                    }

                    objRow["Total"] = objOferta.Total;

                    objDataTable.Rows.Add(objRow);
                }

                return objDataTable.DefaultView;
            }
        }
        #endregion

        #region "Constructor"
        public MatrizHomogeneizacion(List<ProveedorOferente> objProveedores, List<FactorAnalizado> objFactoresAnalizados, Insumo objInsumo, bool booUsarCantidad, decimal decCantidad)
        {
            this.booUsarCantidad = booUsarCantidad;
            this.decCantidad = decCantidad;
            this.objFactores = objFactoresAnalizados;
            this.SetOfertasAnalizadas(objProveedores, objFactoresAnalizados, objInsumo);
        }

        private void SetOfertasAnalizadas(List<ProveedorOferente> objProveedores, List<FactorAnalizado> objFactoresAnalizados, Insumo objInsumo)
        {
            //busco todas las ofertas realizadas por los proveedores
            StringBuilder strSQL = new StringBuilder();

            strSQL.Append("SELECT OFERTAS.IDOFERTA ");
            strSQL.Append("FROM OFERTAS INNER JOIN ");
            strSQL.Append("DETALLEOFERTA ON OFERTAS.IDOFERTA = DETALLEOFERTA.IDOFERTA ");
            strSQL.Append("WHERE FECHAVENCIMIENTO >= GETDATE() AND ");
            strSQL.Append("DETALLEOFERTA.IDINSUMO=" + objInsumo.IdInsumo + " AND ");

            if (objProveedores.Count == 0)
                return;

            strSQL.Append("OFERTAS.IDPROVEEDOR IN (");

            bool booComa = false;
            object[] objValues = new object[objProveedores.Count];

            for (int i = 0; i < objProveedores.Count; i++)
            {
                if (booComa)
                    strSQL.Append(", ");
                else
                    booComa = true;

                strSQL.Append("?");
                objValues[i] = objProveedores[i].IdProveedor;
            }
            strSQL.Append(")");

            try
            {
                //ejecuto la consulta
                SQL sql = new SQL();
                sql.SqlString = strSQL.ToString();
                OleDbDataReader objDR;
                objDR = sql.GetDataReader(objValues);

                if (objDR == null)
                {
                    sql.Close();
                    return;
                }
                if (!objDR.HasRows)
                {
                    sql.Close();
                    return;
                }

                this.objOfertasAnalizadas = new List<OfertaAnalizada>();
                OfertaProveedor objOfertaProveedor;

                //si utiliza factor cantidad, lo agrego a la lista de factores a analizar
                if (this.booUsarCantidad)
                {
                    FactorAnalizado objCantidad = new FactorAnalizado(new Factor(-2, "Cantidad", false));
                    objCantidad.Tolerancia = this.decCantidad;
                    this.objFactores.Add(objCantidad);
                }
                
                //cargo las ofertas a analizar
                while (objDR.Read())
                {
                    OfertaProveedor.getManager.ClearFilters();
                    OfertaProveedor.getManager.AddFilter("IDOFERTA", GenericManager<OfertaProveedor>.filterOperator.Equals, objDR["IdOferta"]);
                    objOfertaProveedor = OfertaProveedor.getManager.GetOne();

                    this.objOfertasAnalizadas.Add(new OfertaAnalizada(objOfertaProveedor, objFactoresAnalizados, objInsumo.IdInsumo));
                }
                objDR.Close();
                sql.Close();
            }
            catch (SQLManagerException ex)
            {
                throw new BussinessLogicTecnoPezException(ex.Message);
            }
            catch 
            {
                throw new BussinessLogicTecnoPezException("Error al generar la información para calcular la matriz de homogeneización");
            }
        }
        #endregion

        #region "Métodos"
        public bool Calcular()
        {
            DescartarPorTolerancia();
            if (this.objOfertasAnalizadas.Count == 0)
            {
                return true;
            }
            this.objMaximosMinimos = new List<FactorAnalizado>();
            this.CalcularMaximosMinimos();         

            CalcularPuntajes();

            return true;
        }

        private void DescartarPorTolerancia()
        {
            if (this.objOfertasAnalizadas == null || this.objOfertasAnalizadas.Count == 0)
                throw new BussinessLogicTecnoPezException("No se encontraron ofertas para analizar.");

            List<OfertaAnalizada> objTemp = new List<OfertaAnalizada>();
            objTemp.AddRange(this.objOfertasAnalizadas);

            if (this.objOfertasDescartadas == null)
                this.objOfertasDescartadas = new List<OfertaDescartada>();

            FactorAnalizado objAnalizado = null;
            foreach (OfertaAnalizada objOferta in this.objOfertasAnalizadas)
            {
                if (this.booUsarCantidad)
                {
                    if (!AceptaTolerancia(objOferta.GetFactorCantidad(), ref objAnalizado))
                    {
                        this.objOfertasDescartadas.Add(new OfertaDescartada(objOferta, objAnalizado));
                        objTemp.Remove(objOferta);
                        continue;
                    }
                }
                foreach (FactorAnalizado objFactor in objOferta.Factores)
                {
                    if (!AceptaTolerancia(objFactor, ref objAnalizado))
                    {
                        //agrega una OfertaDescartada
                        this.objOfertasDescartadas.Add(new OfertaDescartada(objOferta, objAnalizado));
                        objTemp.Remove(objOferta);
                        break;
                    }
                }
            }
            this.objOfertasAnalizadas = objTemp;
        }

        /// <summary>
        /// Verifica si el factor pasa el valor de tolerancia indicado
        /// </summary>
        /// <param name="objFactor">Factor a verificar si cumple la tolerancia</param>
        /// <param name="objFactorAnalizado">Se carga con el factor que no cumple la tolerancia</param>
        /// <returns>True si pasa la tolerancia</returns>
        private bool AceptaTolerancia(FactorAnalizado objFactor, ref FactorAnalizado objFactorAnalizado)
        {
            foreach (FactorAnalizado objAnalizado in this.objFactores)
            {
                if (objFactor.IdFactor == objAnalizado.IdFactor)
                {
                    //si Tolerancia=0 es porque no se asigno
                    if (objAnalizado.Tolerancia != 0)
                    {
                        objFactorAnalizado = objAnalizado;
                        objFactorAnalizado.Valor = objFactor.Valor;
                        //retorna al reves los valores
                        if (objFactor.Maximizante)
                            return objFactor.Valor >= objAnalizado.Tolerancia;
                        else
                            return objFactor.Valor <= objAnalizado.Tolerancia;
                    }
                    break;
                }
            }
            //si no hay tolerancia, entonces es aceptado
            return true;
        }

        private void CalcularMaximosMinimos()
        {
            foreach (OfertaAnalizada objOferta in this.objOfertasAnalizadas)
            {
                foreach (FactorAnalizado objFactor in objOferta.Factores)
                {
                    this.CalcularMaximoMinimo(objFactor);
                }
            }
        }

        private bool CalcularMaximoMinimo(FactorAnalizado objBuscado)
        {
            foreach (FactorAnalizado objFactor in objMaximosMinimos)
            {
                if (objFactor.IdFactor == objBuscado.IdFactor)
                {
                    if (objFactor.Maximizante)
                    {
                        //busca el mayor
                        if (objBuscado.Valor > objFactor.Valor)
                            objFactor.Valor = objBuscado.Valor;
                    }
                    else
                    {
                        //busca el menor
                        if (objBuscado.Valor < objFactor.Valor)
                            objFactor.Valor = objBuscado.Valor;
                    }

                    return true;
                }
            }
            objMaximosMinimos.Add(objBuscado.Clone());
            return true;
        }

        private bool CalcularPuntajes()
        {
            decimal decMaxMinValue = 0;
            foreach (OfertaAnalizada objOferta in this.objOfertasAnalizadas)
            {
                foreach (FactorAnalizado objFactor in objOferta.Factores)
                {
                    decMaxMinValue = GetMaximoMinimoValue(objFactor.IdFactor);
                    if (objFactor.Maximizante)
                    {
                        if (decMaxMinValue != 0)
                            objFactor.Puntaje = Math.Round(objFactor.Valor / decMaxMinValue * GetPuntajeFactor(objFactor.IdFactor), 2);
                        else
                            objFactor.Puntaje = 0;
                    }
                    else
                    {
                        if (objFactor.Valor != 0)
                            objFactor.Puntaje = Math.Round(decMaxMinValue / objFactor.Valor * GetPuntajeFactor(objFactor.IdFactor), 2);
                        else
                            objFactor.Puntaje = 0;
                    }
                }
            }
            return true;
        }

        private decimal GetMaximoMinimoValue(int idFactor)
        {
            foreach (FactorAnalizado objFactor in this.objMaximosMinimos)
            {
                if (objFactor.IdFactor == idFactor)
                    return objFactor.Valor;
            }
            return 0;
        }

        private decimal GetPuntajeFactor(int idFactor)
        {
            foreach (FactorAnalizado objFactor in this.objFactores)
            {
                if (objFactor.IdFactor == idFactor)
                    return objFactor.Puntaje;
            }
            return 0;
        }

        #endregion
    }    
}
